<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>对象的合并</title>
</head>
<body>
<script>
    /**
     * 方法：Object.assign
     * 功能：将【源对象】的所有可枚举的属性，复制到【目标对象】
     * 参数：第一参数是【目标对象】，后面的参数都是源对象
     *
     * */
    const target = { a: 1 };

    const source1 = { b: 2 };
    const source2 = { c: 3 };

    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}

    /// 注意参数的类型

    // 1. 【目标对象】与【源对象】有同名属性，则后面的属性会覆盖前面的属性
    // 2. 多个【源对象】有同名属性，则后面的属性会覆盖前面的属性
    const target = { a: 1, b: 1 };

    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };

    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}
    // 3. 只有一个参数的时候，Object.assign会直接返回该参数
    const obj = {a: 1};
    Object.assign(obj) === obj // true
    // 4. 参数不是对象，它会自动转为对象
    typeof Object.assign(2) // "object"
    // 5. undefined 和 null 无法转成对象，所以如果它们作为参数，报错
    Object.assign(undefined) // 报错
    Object.assign(null) // 报错

    /// 注意参数的位置 : 如果非对象参数出现在【源对象】的位置（即非首参数），那么处理规则有所不同

    // 1. 这些参数都会转成对象，如果无法转成对象，就会跳过
    let obj = {a: 1};// 意味着，如果 undefined 和 null 不在首参数，就不会报错
    Object.assign(obj, undefined) === obj; // true
    Object.assign(obj, null) === obj; // true => 其他类型的值（即数值、字符串和布尔值）不在首参数，也不会报错

    // 2. 但是，除了【字符串】会以数组形式，拷贝入目标对象。其他值都不会产生效果:
    const v1 = 'abc';
    const v2 = true; // 没效果
    const v3 = 10; // 没效果
    /* 这是因为只有字符串的包装对象，会产生可枚举属性
          Object(true) // {[[PrimitiveValue]]: true} 翻译：PrimitiveValue -> 原始值
          Object(10)  //  {[[PrimitiveValue]]: 10}
          Object('abc') // {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}

            以上：布尔值、数值、字符串分别转成对应的包装对象
            它们的【原始值】都在包装对象内部属性[[PrimitiveValue]]上面，这个属性是不会被Object.assign拷贝的
            只有字符串的包装对象，会产生可枚举的实义属性，那些属性则会被拷贝
    * */
    const obj = Object.assign({}, v1, v2, v3);
    console.log(obj); // { "0": "a", "1": "b", "2": "c" } // 数组形式

    // 拷贝注意：1. 只拷贝对象自身的属性【不拷贝自身属性】 2. 不拷贝不可枚举属性【enumerable：false】
    Object.assign({b: 'c'},
      Object.defineProperty({}, 'invisible', { // 翻译：Property 属性 define 限定、明确
        enumerable: false,
        value: 'hello'
      })
    ) // { b: 'c' }

    // 对于 属性名为Symbol值的属性，也会被Object.assign拷贝
    Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' }) // { a: 'b', Symbol(c): 'd' }
</script>
</body>
</html>